package com.example.bjsfpost.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultClientConnectionReuseStrategy;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.springframework.web.multipart.MultipartFile;
import org.apache.http.entity.mime.MultipartEntityBuilder;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * 支持http https忽略ssl验证请求
 * 目前支持post get方式
 */
@Slf4j
public enum HttpUtil {
    INSTANCE;
    /**
     * 发送HTTP请求超时时间,单位:ms
     */
    private final static Integer connTimeOut = 60000;
    /**
     * 请求超时,单位:ms
     */
    private final static Integer reqTimeOut = 60000;
    /**
     * 响应传输数据超时,单位:ms
     */
    private final static Integer resTimeOut = 60000;

    /**
     * Post String
     *
     * @param url
     * @param headers
     * @param request
     * @return byte[]
     */
    public static String sendPost(String url, Map<String, String> headers, String request) {
        HttpPost httpPost = new HttpPost(url);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> e : headers.entrySet()) {
                httpPost.addHeader(e.getKey(), e.getValue());
            }
        }
        StringEntity stringEntity = new StringEntity(request, StandardCharsets.UTF_8);
        httpPost.setEntity(stringEntity);
        return executeHttpRequest(httpPost, url);
    }

    public static String sendPost(String url, Map<String, String> header, Map<String, String> body) {
        HttpPost httpPost = new HttpPost(url);
        // 设置请求头
        if (header != null && !header.isEmpty()) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpPost.setHeader(entry.getKey().toLowerCase(), entry.getValue());
            }
        }
        // 设置请求体
        if (body != null && !body.isEmpty()) {
            List<NameValuePair> formParams = new ArrayList<>();
            for (Map.Entry<String, String> entry : body.entrySet()) {
                formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8);
            httpPost.setEntity(entity);
        }
        return executeHttpRequest(httpPost, url);
    }

    public static String sendGet(String url, Map<String, String> header) {
        HttpGet httpGet = new HttpGet(url);
        if (header != null && !header.isEmpty()) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpGet.setHeader(entry.getKey(), entry.getValue());
            }
        }
        return executeHttpRequest(httpGet, url);
    }

    public static <T> String executeHttpRequest(T httpReq, String url) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        if (httpReq instanceof HttpGet) {
            HttpGet httpGet = (HttpGet) httpReq;
            httpGet.setConfig(timeOutConfig());
            try {
                httpClient = wrapClient(url);
                response = httpClient.execute(httpGet);
                HttpEntity entity = response.getEntity();
                return EntityUtils.toString(entity, "UTF-8");
            } catch (Exception e) {
                log.error("异常===={}", e);
                throw new RuntimeException(e);
            } finally {
                closeConnect(response, httpClient);
            }
        }
        if (httpReq instanceof HttpPost) {
            HttpPost httpPost = (HttpPost) httpReq;
            httpPost.setConfig(timeOutConfig());
            try {
                httpClient = wrapClient(url);
                response = httpClient.execute(httpPost);
                HttpEntity entity = response.getEntity();
                return EntityUtils.toString(entity);
            } catch (Exception e) {
                log.error("异常===={}", e);
                throw new RuntimeException(e);
            } finally {
                closeConnect(response, httpClient);
            }
        }
        return null;
    }

    /**
     * 获取 HttpClient
     *
     * @param url
     * @return
     */
    //全局参数
    private static PoolingHttpClientConnectionManager connectionManager;
    //设置请求参数
    private static RequestConfig config;

    private static CloseableHttpClient client;

    //单例模式创建
    private static void init() {
        synchronized (HttpUtil.class) {
            try {
                // 在调用SSL之前需要重写验证方法，取消检测SSL
                X509TrustManager trustManager = new X509TrustManager() {
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    @Override
                    public void checkClientTrusted(X509Certificate[] xcs, String str) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] xcs, String str) {
                    }
                };
                SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
                ctx.init(null, new TrustManager[]{trustManager}, null);
                SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
                // 创建Registry
                RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)
                        .setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
                        .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).setConnectTimeout(120000) //连接超时时间
                        .setConnectionRequestTimeout(120_000) //从线程池中获取线程超时时间
                        .setSocketTimeout(120_000) //设置数据超时时间
                        .build();
                Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.INSTANCE)
                        .register("https", socketFactory).build();
                // 创建ConnectionManager，添加Connection配置信息
                PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
                connectionManager.setMaxTotal(220); // 设置连接池最大连接数为100
                connectionManager.setDefaultMaxPerRoute(100); // 设置每个路由最大连接数为100
                connectionManager.setValidateAfterInactivity(120_000); // 设置连接池超时时间为120秒
                connectionManager.setDefaultSocketConfig(SocketConfig.custom()
                        .setSoTimeout(120_000)
                        .build());
                CloseableHttpClient closeableHttpClient =
                        HttpClients.custom()
                                .setConnectionManager(connectionManager)
                                .setDefaultRequestConfig(requestConfig)
                                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
                                .setConnectionReuseStrategy(new DefaultClientConnectionReuseStrategy())
                                .setConnectionManagerShared(true)
                                .build();
                client = closeableHttpClient;
            } catch (KeyManagementException ex) {
                throw new RuntimeException(ex);
            } catch (NoSuchAlgorithmException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    /**
     * 从池子中获取连接
     *
     * @return CloseableHttpClient
     */
    public static CloseableHttpClient wrapClient(String url) {
        if (client == null) {
            init();
        }
        return client;
    }


    /**
     * 设置 连接超时、 请求超时 、 读取超时  毫秒
     *
     * @return
     */
    private static RequestConfig timeOutConfig() {
        return RequestConfig.custom()
                .setConnectionRequestTimeout(reqTimeOut)
                .setConnectTimeout(connTimeOut)
                .setSocketTimeout(resTimeOut)
                .build();
    }

    /**
     * 关闭链接
     *
     * @param response   响应
     * @param httpClient 客户端
     */
    private static void closeConnect(HttpResponse response, HttpClient httpClient) {
        // 关闭响应链接
        HttpClientUtils.closeQuietly(response);
        // 关闭请求链接
        //HttpClientUtils.closeQuietly(httpClient);
    }

   /* public static String httpPost(String url, Map<String, String> params, Map<String, File> files) {
        //HttpPost请求实体
        HttpPost httpPost = new HttpPost(url);
        //使用工具类创建 httpClient
        CloseableHttpClient client = HttpClients.createDefault();
        CloseableHttpResponse resp = null;
        String respondBody = null;
        try {
            //设置请求超时时间和 sockect 超时时间
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(200000).setSocketTimeout(200000000).build();
            httpPost.setConfig(requestConfig);
            //附件参数需要用到的请求参数实体构造器
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();

            if (null != files & !files.isEmpty()) {
                files.forEach((name, file) -> {
                    //附件参数,name对应请求参数的key值，file为文件
                    //添加文件参数，分隔符号会被自动设置，我们无须关注
                    multipartEntityBuilder.addBinaryBody(name, file);
                });
            }
            if (null != params & !params.isEmpty()) {
                params.forEach((key, value) -> {
                    //此处的字符串参数会被设置到请求体Query String Parameters中
                    multipartEntityBuilder.addTextBody(key, value);
                });
            }
            HttpEntity httpEntity = multipartEntityBuilder.build();
            //将请求参数放入 HttpPost 请求体中
            //使用 httpEntity 后 Content-Type会自动被设置成 multipart/form-data
            httpPost.setEntity(httpEntity);
            //执行发送post请求
            resp = client.execute(httpPost);
            //将返回结果转成String
            respondBody = EntityUtils.toString(resp.getEntity());
        } catch (IOException | ParseException e) {
            //日志信息及异常处理
            String msg = "执行HTTP响应时抛出异常，需要关注";
        } finally {
        }
        return respondBody;
    }
    public static Map<String, String> doPostFile(String url, List<MultipartFile> multipartFiles,String fileParName,
                                                 Map<String, Object> params, int timeout) {
        Map<String, String> resultMap = new HashMap<String, String>();
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String result = "";
        try {
            HttpPost httpPost = new HttpPost(url);
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(java.nio.charset.Charset.forName("UTF-8"));
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            String fileName = null;
            MultipartFile multipartFile = null;
            for (int i = 0; i < multipartFiles.size(); i++) {
                multipartFile = multipartFiles.get(i);
                fileName = multipartFile.getOriginalFilename();
                builder.addBinaryBody(fileParName, multipartFile.getInputStream(), ContentType.MULTIPART_FORM_DATA, fileName);// 文件流
            }
            //决中文乱码
            ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8);
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if(entry.getValue() == null)
                    continue;
                // 类似浏览器表单提交，对应input的name和value
                builder.addTextBody(entry.getKey(), entry.getValue().toString(), contentType);
            }
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            HttpResponse response = httpClient.execute(httpPost);// 执行提交

            // 设置连接超时时间
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeout)
                    .setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build();
            httpPost.setConfig(requestConfig);

            HttpEntity responseEntity = response.getEntity();
            resultMap.put("scode", String.valueOf(response.getStatusLine().getStatusCode()));
            resultMap.put("data", "");
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, java.nio.charset.Charset.forName("UTF-8"));
                resultMap.put("data", result);
            }
        } catch (Exception e) {
            resultMap.put("scode", "error");
            resultMap.put("data", "HTTP请求出现异常: " + e.getMessage());

            Writer w = new StringWriter();
            e.printStackTrace(new PrintWriter(w));
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultMap;
    }*/
}
